1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package sun.security.krb5.internal.tools;
32
33 import sun.security.krb5.*;
34 import sun.security.krb5.internal.ktab.*;
35 import java.io.IOException;
36 import java.io.BufferedReader;
37 import java.io.InputStreamReader;
38 import java.io.File;
39 import java.text.DateFormat;
40 import java.util.Arrays;
41 import java.util.Date;
42 import java.util.Locale;
43 import sun.security.krb5.internal.crypto.EType;
44
45
46
47
48
49
50
51
52
53 public class Ktab {
54
55 KeyTab table;
56 char action;
57 String name;
58 String principal;
59 boolean showEType;
60 boolean showTime;
61 int etype = -1;
62 char[] password = null;
63
64 boolean forced = false;
65 boolean append = false;
66 int vDel = -1;
67 int vAdd = -1;
68
69
70
71
72
73 public static void main(String[] args) {
74 Ktab ktab = new Ktab();
75 if ((args.length == 1) && (args[0].equalsIgnoreCase("-help"))) {
76 ktab.printHelp();
77 return;
78 } else if ((args == null) || (args.length == 0)) {
79 ktab.action = 'l';
80 } else {
81 ktab.processArgs(args);
82 }
83 try {
84 if (ktab.name == null) {
85
86 ktab.table = KeyTab.getInstance();
87 if (ktab.table == null) {
88 if (ktab.action == 'a') {
89 ktab.table = KeyTab.create();
90 } else {
91 System.out.println("No default key table exists.");
92 System.exit(-1);
93 }
94 }
95 } else {
96 if ((ktab.action != 'a') &&
97 !(new File(ktab.name)).exists()) {
98 System.out.println("Key table " +
99 ktab.name + " does not exist.");
100 System.exit(-1);
101 } else {
102 ktab.table = KeyTab.getInstance(ktab.name);
103 }
104 if (ktab.table == null) {
105 if (ktab.action == 'a') {
106 ktab.table = KeyTab.create(ktab.name);
107 } else {
108 System.out.println("The format of key table " +
109 ktab.name + " is incorrect.");
110 System.exit(-1);
111 }
112 }
113 }
114 } catch (RealmException e) {
115 System.err.println("Error loading key table.");
116 System.exit(-1);
117 } catch (IOException e) {
118 System.err.println("Error loading key table.");
119 System.exit(-1);
120 }
121 switch (ktab.action) {
122 case 'l':
123 ktab.listKt();
124 break;
125 case 'a':
126 ktab.addEntry();
127 break;
128 case 'd':
129 ktab.deleteEntry();
130 break;
131 default:
132 ktab.error("A command must be provided");
133 }
134 }
135
136
137
138
139 void processArgs(String[] args) {
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157 boolean argAlreadyAppeared = false;
158 for (int i = 0; i < args.length; i++) {
159 if (args[i].startsWith("-")) {
160 switch (args[i].toLowerCase(Locale.US)) {
161
162
163 case "-l":
164 action = 'l';
165 break;
166 case "-a":
167 action = 'a';
168 if (++i >= args.length || args[i].startsWith("-")) {
169 error("A principal name must be specified after -a");
170 }
171 principal = args[i];
172 break;
173 case "-d":
174 action = 'd';
175 if (++i >= args.length || args[i].startsWith("-")) {
176 error("A principal name must be specified after -d");
177 }
178 principal = args[i];
179 break;
180
181
182 case "-e":
183 if (action == 'l') {
184 showEType = true;
185 } else if (action == 'd') {
186 if (++i >= args.length || args[i].startsWith("-")) {
187 error("An etype must be specified after -e");
188 }
189 try {
190 etype = Integer.parseInt(args[i]);
191 if (etype <= 0) {
192 throw new NumberFormatException();
193 }
194 } catch (NumberFormatException nfe) {
195 error(args[i] + " is not a valid etype");
196 }
197 } else {
198 error(args[i] + " is not valid after -" + action);
199 }
200 break;
201 case "-n":
202 if (++i >= args.length || args[i].startsWith("-")) {
203 error("A KVNO must be specified after -n");
204 }
205 try {
206 vAdd = Integer.parseInt(args[i]);
207 if (vAdd < 0) {
208 throw new NumberFormatException();
209 }
210 } catch (NumberFormatException nfe) {
211 error(args[i] + " is not a valid KVNO");
212 }
213 break;
214 case "-k":
215 if (++i >= args.length || args[i].startsWith("-")) {
216 error("A keytab name must be specified after -k");
217 }
218 if (args[i].length() >= 5 &&
219 args[i].substring(0, 5).equalsIgnoreCase("FILE:")) {
220 name = args[i].substring(5);
221 } else {
222 name = args[i];
223 }
224 break;
225 case "-t":
226 showTime = true;
227 break;
228 case "-f":
229 forced = true;
230 break;
231 case "-append":
232 append = true;
233 break;
234 default:
235 error("Unknown command: " + args[i]);
236 break;
237 }
238 } else {
239 if (argAlreadyAppeared) {
240 error("Useless extra argument " + args[i]);
241 }
242 if (action == 'a') {
243 password = args[i].toCharArray();
244 } else if (action == 'd') {
245 switch (args[i]) {
246 case "all": vDel = -1; break;
247 case "old": vDel = -2; break;
248 default: {
249 try {
250 vDel = Integer.parseInt(args[i]);
251 if (vDel < 0) {
252 throw new NumberFormatException();
253 }
254 } catch (NumberFormatException nfe) {
255 error(args[i] + " is not a valid KVNO");
256 }
257 }
258 }
259 } else {
260 error("Useless extra argument " + args[i]);
261 }
262 argAlreadyAppeared = true;
263 }
264 }
265 }
266
267
268
269
270
271
272 void addEntry() {
273 PrincipalName pname = null;
274 try {
275 pname = new PrincipalName(principal);
276 if (pname.getRealm() == null) {
277 pname.setRealm(Config.getInstance().getDefaultRealm());
278 }
279 } catch (KrbException e) {
280 System.err.println("Failed to add " + principal +
281 " to keytab.");
282 e.printStackTrace();
283 System.exit(-1);
284 }
285 if (password == null) {
286 try {
287 BufferedReader cis =
288 new BufferedReader(new InputStreamReader(System.in));
289 System.out.print("Password for " + pname.toString() + ":");
290 System.out.flush();
291 password = cis.readLine().toCharArray();
292 } catch (IOException e) {
293 System.err.println("Failed to read the password.");
294 e.printStackTrace();
295 System.exit(-1);
296 }
297
298 }
299 try {
300
301 table.addEntry(pname, password, vAdd, append);
302 Arrays.fill(password, '0');
303
304 table.save();
305 System.out.println("Done!");
306 System.out.println("Service key for " + principal +
307 " is saved in " + table.tabName());
308
309 } catch (KrbException e) {
310 System.err.println("Failed to add " + principal + " to keytab.");
311 e.printStackTrace();
312 System.exit(-1);
313 } catch (IOException e) {
314 System.err.println("Failed to save new entry.");
315 e.printStackTrace();
316 System.exit(-1);
317 }
318 }
319
320
321
322
323 void listKt() {
324 System.out.println("Keytab name: " + table.tabName());
325 KeyTabEntry[] entries = table.getEntries();
326 if ((entries != null) && (entries.length > 0)) {
327 String[][] output = new String[entries.length+1][showTime?3:2];
328 int column = 0;
329 output[0][column++] = "KVNO";
330 if (showTime) output[0][column++] = "Timestamp";
331 output[0][column++] = "Principal";
332 for (int i = 0; i < entries.length; i++) {
333 column = 0;
334 output[i+1][column++] = entries[i].getKey().
335 getKeyVersionNumber().toString();
336 if (showTime) output[i+1][column++] =
337 DateFormat.getDateTimeInstance(
338 DateFormat.SHORT, DateFormat.SHORT).format(
339 new Date(entries[i].getTimeStamp().getTime()));
340 String princ = entries[i].getService().toString();
341 if (showEType) {
342 int e = entries[i].getKey().getEType();
343 output[i+1][column++] = princ + " (" + e + ":" +
344 EType.toString(e) + ")";
345 } else {
346 output[i+1][column++] = princ;
347 }
348 }
349 int[] width = new int[column];
350 for (int j=0; j<column; j++) {
351 for (int i=0; i <= entries.length; i++) {
352 if (output[i][j].length() > width[j]) {
353 width[j] = output[i][j].length();
354 }
355 }
356 if (j != 0) width[j] = -width[j];
357 }
358 for (int j=0; j<column; j++) {
359 System.out.printf("%" + width[j] + "s ", output[0][j]);
360 }
361 System.out.println();
362 for (int j=0; j<column; j++) {
363 for (int k=0; k<Math.abs(width[j]); k++) System.out.print("-");
364 System.out.print(" ");
365 }
366 System.out.println();
367 for (int i=0; i<entries.length; i++) {
368 for (int j=0; j<column; j++) {
369 System.out.printf("%" + width[j] + "s ", output[i+1][j]);
370 }
371 System.out.println();
372 }
373 } else {
374 System.out.println("0 entry.");
375 }
376 }
377
378
379
380
381 void deleteEntry() {
382 PrincipalName pname = null;
383 try {
384 pname = new PrincipalName(principal);
385 if (pname.getRealm() == null) {
386 pname.setRealm(Config.getInstance().getDefaultRealm());
387 }
388 if (!forced) {
389 String answer;
390 BufferedReader cis =
391 new BufferedReader(new InputStreamReader(System.in));
392 System.out.print("Are you sure you want to delete "+
393 "service key(s) for " + pname.toString() +
394 " (" + (etype==-1?"all etypes":("etype="+etype)) + ", " +
395 (vDel==-1?"all kvno":(vDel==-2?"old kvno":("kvno=" + vDel))) +
396 ") in " + table.tabName() + "? (Y/[N]): ");
397
398 System.out.flush();
399 answer = cis.readLine();
400 if (answer.equalsIgnoreCase("Y") ||
401 answer.equalsIgnoreCase("Yes"));
402 else {
403
404 System.exit(0);
405 }
406 }
407 } catch (KrbException e) {
408 System.err.println("Error occured while deleting the entry. "+
409 "Deletion failed.");
410 e.printStackTrace();
411 System.exit(-1);
412 } catch (IOException e) {
413 System.err.println("Error occured while deleting the entry. "+
414 " Deletion failed.");
415 e.printStackTrace();
416 System.exit(-1);
417 }
418
419 int count = table.deleteEntries(pname, etype, vDel);
420
421 if (count == 0) {
422 System.err.println("No matched entry in the keytab. " +
423 "Deletion fails.");
424 System.exit(-1);
425 } else {
426 try {
427 table.save();
428 } catch (IOException e) {
429 System.err.println("Error occurs while saving the keytab. " +
430 "Deletion fails.");
431 e.printStackTrace();
432 System.exit(-1);
433 }
434 System.out.println("Done! " + count + " entries removed.");
435 }
436 }
437
438 void error(String... errors) {
439 for (String error: errors) {
440 System.out.println("Error: " + error + ".");
441 }
442 printHelp();
443 System.exit(-1);
444 }
445
446
447
448 void printHelp() {
449 System.out.println("\nUsage: ktab <commands> <options>");
450 System.out.println();
451 System.out.println("Available commands:");
452 System.out.println();
453 System.out.println("-l [-e] [-t]\n"
454 + " list the keytab name and entries. -e with etype, -t with timestamp.");
455 System.out.println("-a <principal name> [<password>] [-n <kvno>] [-append]\n"
456 + " add new key entries to the keytab for the given principal name with\n"
457 + " optional <password>. If a <kvno> is specified, new keys' Key Version\n"
458 + " Numbers equal to the value, otherwise, automatically incrementing\n"
459 + " the Key Version Numbers. If -append is specified, new keys are\n"
460 + " appended to the keytab, otherwise, old keys for the\n"
461 + " same principal are removed.");
462 System.out.println("-d <principal name> [-f] [-e <etype>] [<kvno> | all | old]\n"
463 + " delete key entries from the keytab for the specified principal. If\n"
464 + " <kvno> is specified, delete keys whose Key Version Numbers match\n"
465 + " kvno. If \"all\" is specified, delete all keys. If \"old\" is specified,\n"
466 + " delete all keys except those with the highest kvno. Default action\n"
467 + " is \"all\". If <etype> is specified, only keys of this encryption type\n"
468 + " are deleted. <etype> should be specified as the numberic value etype\n"
469 + " defined in RFC 3961, section 8. A prompt to confirm the deletion is\n"
470 + " displayed unless -f is specified.");
471 System.out.println();
472 System.out.println("Common option(s):");
473 System.out.println();
474 System.out.println("-k <keytab name>\n"
475 + " specify keytab name and path with prefix FILE:");
476 }
477 }